﻿using System;
using System.Collections.Generic;
using Compiler.Primitives;

namespace Compiler.Modules
{
    class Preprocessor
    {
        private string[] _code;

        public void Run(string[] code)
        {
            _code = code;

            ImportLibraries();
            FindClasses();
        }

        private void ImportLibraries()
        {
            foreach (var line in _code)
            {
                if (StringParser.GetKeyword(line).ToUpper() == "USE")
                {
                    var parameters = StringParser.GetParameters(line, true);

                    if (parameters.Length != 1)
                    {
                        throw new Exception("Incorrect number of parameters: " + line);
                    }

                    //Do stuff here to import the class
                    //Do we want to append the code to the bottom of the currently loaded file?
                }
            }
        }

        private void FindClasses()
        {
            var className = "";
            var inClass = false;
            var bracketCount = 0;
            var classCode = "";

            foreach (var line in _code)
            {
                if (StringParser.GetKeyword(line).ToUpper() == "CLASS")
                {
                    var parameters = StringParser.GetParameters(line, true);

                    if (parameters.Length != 1)
                    {
                        throw new Exception("Incorrect number of parameters: " + line);
                    }

                    className = StringParser.GetWord(parameters[0], 0);

                    bracketCount = StringParser.CountSymbolOccurances(line, '{') -
                                   StringParser.CountSymbolOccurances(line, '}');

                    inClass = true;
                    continue;
                }

                if (inClass)
                {
                    bracketCount += StringParser.CountSymbolOccurances(line, '{') -
                                    StringParser.CountSymbolOccurances(line, '}');

                    if (bracketCount <= 0)
                    {
                        classCode += line.Substring(0, line.LastIndexOf('}'));
                        Hub.AddClass(new Class(className, FindFunctions(classCode)));

                        className = "";
                        classCode = "";
                        bracketCount = 0;
                        inClass = false;
                    }
                    else
                    {
                        classCode += line + '\n';
                    }
                }
            }
        }

        public List<Function> FindFunctions(string code)
        {
            var lines = code.Split('\n');
            var functionName = "";
            var inFunction = false;
            var bracketCount = 0;
            var functionCode = "";

            var functions = new List<Function>();

            foreach (var line in lines)
            {

                if (StringParser.GetKeyword(line).ToUpper() == "FUNCTION")
                {
                    var parameters = StringParser.GetParameters(line, true);

                    if (parameters.Length != 1)
                    {
                        throw new Exception("Incorrect number of parameters: " + line);
                    }

                    functionName = parameters[0];

                    bracketCount = StringParser.CountSymbolOccurances(line, '{') -
                                   StringParser.CountSymbolOccurances(line, '}');

                    inFunction = true;
                    continue;
                }

                if (inFunction)
                {
                    bracketCount += StringParser.CountSymbolOccurances(line, '{') -
                                    StringParser.CountSymbolOccurances(line, '}');

                    if (bracketCount <= 0)
                    {
                        functionCode += line.Substring(0, line.LastIndexOf('}'));
                        var name = functionName.Substring(0, functionName.IndexOf('('));
                        var parameters = StringParser.GetParameters(StringParser.GetBracketContents(functionName));

                        var args = new List<Argument>();

                        foreach (var arg in parameters)
                        {
                            if (StringParser.GetWordCount(arg) != 2)
                            {
                                throw new Exception("Invalid argument");
                            }

                            var parts = StringParser.GetWords(arg);
                            Argument.ArgType type;

                            switch (parts[0].ToUpper())
                            {
                                case "STRING":
                                    type = Argument.ArgType.String;
                                    break;

                                case "STRING[]":
                                    type = Argument.ArgType.StringArray;
                                    break;

                                case "INT":
                                    type = Argument.ArgType.Int;
                                    break;

                                case "INT[]":
                                    type = Argument.ArgType.IntArray;
                                    break;

                                case "FLOAT":
                                    type = Argument.ArgType.Float;
                                    break;

                                case "FLOAT[]":
                                    type = Argument.ArgType.FloatArray;
                                    break;

                                case "BOOLEAN":
                                    type = Argument.ArgType.Boolean;
                                    break;

                                case "BOOLEAN[]":
                                    type = Argument.ArgType.BooleanArray;
                                    break;

                                default:
                                    throw new Exception("Unrecognized data type");
                            }

                            args.Add(new Argument(type, parts[1].Trim()));

                        }

                        functions.Add(new Function(name, args, functionCode));

                        functionName = "";
                        functionCode = "";
                        bracketCount = 0;
                        inFunction = false;
                    }
                    else
                    {
                        functionCode += line + '\n';
                    }
                }
            }

            return functions;
        }
    }
}
